<!DOCTYPE html>
  <html>
  <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
      <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
      <title>节点规则帮助</title>
      <style>
      table {
        font-family: arial, sans-serif;
        border-collapse: collapse;
      }

      td,
      th {
        border: 1px solid #DDDDDD;
        text-align: left;
        padding: 8px;
      }

      tr:nth-child(even) {
        background-color: #DDDDDD;
      }
      </style>
  </head>
  <body class="layui-layout-body">
      <h1>流程审批相关接口文档</h1>
      <table>
          <thead>
          <tr>
              <th>接口名称</th>
              <th>接口说明</th>
          </tr>
          </thead>
          <tbody>
          <tr>
              <td>取审批人回调接口</td>
              <td><a href="audit_fetch_audit_user_api.html" target="_blank">查看</a></td>
          </tr>
          <tr>
              <td>流程正常完成回调接口</td>
              <td><a href="audit_flow_complete_call_back_api.html" target="_blank">查看</a></td>
          </tr>
          </tbody>
      </table>

      <h1>流程取审批人实现示例</h1>
      代码示例使用SpringMvc + Mybatis Plus
      <pre class="layui-code" lay-title="取审批人Controller">
    @Api(tags = "流程回调审批人")
    @RestController
    @RequestMapping(value = "/flow/audit")
    public class AssignCallbackController {

        @Autowired
        private AssignCallbackService assignCallbackService;

        @ApiOperation("取审批人回调")
        @PostMapping(value = "user-search")
        public FetchAssignResult fetchAssign(@RequestBody FetchAssignCallBackRo fetchAssignCallBackRo){
            return assignCallbackService.getFetchAssign(fetchAssignCallBackRo);
        }
    }
      </pre>

      <pre class="layui-code" lay-title="取审批人Service">
    @Service
    public class AssignCallbackService {

        @Autowired
        private ISysUserService sysUserService;

        public FetchAssignResult getFetchAssign(FetchAssignCallBackRo fetchAssignCallBackRo) {
            FetchAssignResult fetchAssignResult = new FetchAssignResult();

            String startUserDeptId = fetchAssignCallBackRo.getAuditUserSearchCallBackData();

            List&lt;FetchAssignResult.Resource&gt; resources = new ArrayList&lt;&gt;();
            for (FetchAssignCallBackRo.Resource resource : fetchAssignCallBackRo.getResources()) {
                ResourceType type = resource.getType();
                String dataScope = resource.getDataScope();
                String applyScope = resource.getApplyScope();
                String roleId = resource.getResourceId();
                Boolean multiInstance = resource.getMultiInstance();
                Integer priority = resource.getPriority();

                if (type != ResourceType.GROUP) {
                    continue;
                }

                // 计算审批人，具体的根据自己的业务系统来实现
                // 基本逻辑是：如果dataScope(指定的机构)不为空就使用指定的机构为参照来找指定dataScope(查找范围如本部门)有指定角色(roleId)的人
                //            如果dataScope(指定的机构)为空就使用startUserDeptId(发起人部门)为参照来找指定dataScope(查找范围如本部门)有指定角色(roleId)的人
                List&lt;SysUser&gt; users = sysUserService.calculateAuditUsers(startUserDeptId, roleId, dataScope, applyScope);

                // 输出审批人
                for (SysUser user : users) {
                    FetchAssignResult.Resource returnResource = new FetchAssignResult.Resource();

                    returnResource.setType(ResourceType.USER);
                    returnResource.setResourceId(user.getId());
                    returnResource.setRecipient(user.getEmail());
                    returnResource.setPhone(user.getPhone());
                    returnResource.setResourceName(user.getRealname());
                    returnResource.setMultiInstance(multiInstance);
                    returnResource.setPriority(priority);

                    resources.add(returnResource);
                }
            }

            // 计算自定义指派资源，在节点上可以配置一个自定义的文本，此处将文本定义为Json的格式
            // Json中有一个属性是 processClass(处理类)，这里会从Spring中取这为这个类型的bean，并判断是否为ILocalCustomAssignCallbackService的子类
            // 如果满足则调用ILocalCustomAssignCallbackService接口指定的fetchAuditResources方法
            String customData = fetchAssignCallBackRo.getCustomData();
            if (StringUtils.isNotBlank(customData) && JSONObject.isValid(customData)) {
                JSONObject parsedObject = JSONObject.parseObject(customData);
                // 解析processClass
                String processClass = parsedObject.getString("processClass");
                if (StringUtils.isBlank(processClass)) {
                    throw new RuntimeException("自定义指派规则中processClass属性必须定义");
                }

                // 解析params
                JSONObject paramsJsonObject = parsedObject.getJSONObject("params");
                if (paramsJsonObject == null) {
                    throw new RuntimeException("自定义指派规则中params属性必须定义");
                }

                try {
                    Object callBackService = SpringBeanUtil.getBeanByClassName(processClass);

                    if (callBackService instanceof ILocalCustomAssignCallbackService) {
                        ILocalCustomAssignCallbackService localCustomAssignCallbackService = (ILocalCustomAssignCallbackService) callBackService;
                        // 计算资源
                        resources.addAll(localCustomAssignCallbackService.fetchAuditResources(fetchAssignCallBackRo, paramsJsonObject.getInnerMap()));
                    } else {
                        throw new RuntimeException(String.format("自定义指派规则中processClass类[%s]必须实现ILocalCustomAssignCallbackService接口", processClass));
                    }
                } catch (Exception ex) {
                    throw new RuntimeException(String.format("执行自定义审批资源类processClass[%s]出错, 错误信息: %s", processClass, ex.getMessage()));
                }
            }

            fetchAssignResult.setResources(resources);
            fetchAssignResult.setSuccess(true);

            return fetchAssignResult;
        }
    }
      </pre>

      <a name="completeCallback" ><h1>流程正常完成回调接口</h1></a>
      <pre class="layui-code" lay-title="流程正常完成回调接口Controller">
    @Api(tags = "流程完成回调")
    @RestController
    public class ProcessEndCallBackController {
        @Autowired
        private ProcessEndCallBackService processEndCallBackService;

        @ApiOperation("处理完成回调")
        @PostMapping(value = "/flow/{flowInstanceId}/call-back/complete")
        public ResponseMessage processEndCallBack(@RequestBody ProcessEndCallBackRo processEndCallBackRo, @PathVariable(value = "flowInstanceId") String flowInstanceId) {
            processEndCallBackService.processEndCallBack(processEndCallBackRo, flowInstanceId);

            return ResponseMessage.success();
        }
    }
      </pre>

      <pre class="layui-code" lay-title="流程正常完成回调Service">
    @Service
    public class ProcessEndCallBackService {

        /**
         * 处理完成回调
         */
        public void processEndCallBack(ProcessEndCallBackRo processEndCallBackRo, String processInstanceId) {
            String callBackProcessClass = processEndCallBackRo.getCallBackProcessClass();
            if (StringUtils.isNotBlank(callBackProcessClass)) {
                try {
                    Object callBackService = SpringBeanUtil.getBeanByClassName(callBackProcessClass);

                    if (callBackService instanceof ILocalProcessCompleteCallbackService) {
                        ILocalProcessCompleteCallbackService localProcessCompleteCallbackService = (ILocalProcessCompleteCallbackService) callBackService;
                        // 执行完成回调
                        localProcessCompleteCallbackService.doCompleteCallback(processEndCallBackRo, processInstanceId);
                    } else {
                        throw new RuntimeException(String.format("流程完成回调类[%s]必须实现ILocalProcessCompleteCallbackService接口", callBackProcessClass));
                    }
                } catch (Exception ex) {
                    throw new RuntimeException(String.format("执行流程完成回调类[%s]出错, 错误信息: %s", callBackProcessClass, ex.getMessage()));
                }
            } else {
                throw new RuntimeException("流程完成回调类不能为空");
            }
        }
    }
      </pre>

      <script src="../common/layui/layui.js"></script>
      <script>
          //JavaScript代码区域
          layui.use('code', function(){
              layui.code({
                  title : '代码示例',
                  about : false
              });

          });
      </script>

  </body>
  </html>
